package com.sun.gis.tools.wmts;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 测试访问在线切片服务（TMS），并下载矢量在可视范围内缩放居中的相关切片
 */
public class TileCalculatorForWMTS {

    // 用于保存下载的切片的列表
    public static List<BufferedImage> downloadedTiles = new ArrayList<>();



    /**
     * 将多个WMTS瓦片图像拼接成一个完整的图像。
     *
     * @param startX         拼接区域的起始X坐标
     * @param startY         拼接区域的起始Y坐标
     * @param endX           拼接区域的结束X坐标
     * @param endY           拼接区域的结束Y坐标
     * @param zoom           瓦片的缩放级别
     * @param outputFilePath 输出图像的保存路径
     */
    public static BufferedImage stitchWmtsTiles(int startX, int startY, int endX, int endY, int zoom, String outputFilePath) {
        int tileWidth = 256;
        int tileHeight = 256;

        double tileRes = 360.0 / Math.pow(2, zoom);
        double[] topLeft = tile2latlon(startX, startY, zoom);
        double[] bottomRight = tile2latlon(endX + 1, endY + 1, zoom);

        System.out.println("Top-left lat-lon: " + topLeft[1] + ", " + topLeft[0]);
        System.out.println("Bottom-right lat-lon: " + bottomRight[1] + ", " + bottomRight[0]);



        int outputImageWidth = (endX - startX + 1) * tileWidth;
        int outputImageHeight = (endY - startY + 1) * tileHeight;
        BufferedImage outputImage = new BufferedImage(outputImageWidth, outputImageHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = outputImage.createGraphics();

        for (int x = startX; x <= endX; x++) {
            for (int y = startY; y <= endY; y++) {
                String tilePath = String.format("E:\\code\\gitee\\gis-server\\data\\output\\wmts\\wmts\\%d_%d_%d.png", zoom, x, y);
                BufferedImage tile;
                try {
                    tile = ImageIO.read(new File(tilePath));
                    int xPos = (x - startX) * tileWidth;
                    int yPos = (y - startY) * tileHeight;
                    g.drawImage(tile, xPos, yPos, null);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        g.dispose();

        try {
            ImageIO.write(outputImage, "png", new File(outputFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return outputImage;
    }

    /**
     * 根据给定的WMTS瓦片的x、y和分辨率，转换为其左上角的经纬度坐标。
     *
     * @param x 瓦片的x坐标
     * @param y 瓦片的y坐标
     * @param tileRes 瓦片的分辨率
     * @return 一个double数组，其中第一个元素是经度，第二个是纬度
     */
    public static double[] tile2latlon(int x, int y, double tileRes) {
        // WMTS瓦片使用的是球面墨卡托（EPSG:3857）投影
        // 下面的公式用于将瓦片坐标转换为经纬度坐标
        double n = Math.PI - (2.0 * Math.PI * y) / (360.0 / tileRes);
        double lon = ((x / (360.0 / tileRes) * 360.0) - 180.0);
        double lat = (180.0 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));

        return new double[]{lon, lat};
    }

    /**
     * 根据给定的WMTS瓦片的x、y和缩放级别，转换为其左上角的经纬度坐标。
     *
     * @param x    瓦片的x坐标
     * @param y    瓦片的y坐标
     * @param zoom 缩放级别
     * @return 一个double数组，其中第一个元素是经度，第二个是纬度
     */
    public static double[] tile2latlon(int x, int y, int zoom) {
        double n = Math.pow(2, zoom);
        double lonDeg = x / n * 360.0 - 180.0;
        double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n)));
        double latDeg = Math.toDegrees(latRad);

        return new double[]{lonDeg, latDeg};
    }



    /**
     * 计算矢量多边形最适合的缩放层级
     *
     * @param minX      矢量最小经度
     * @param maxX      矢量最大经度
     * @param minY      矢量最小纬度
     * @param maxY      矢量最大纬度
     * @param imgWidth  视图宽度
     * @param imgHeight 视图高度
     * @return 最适合的层级
     */
    public static int calculateZoomLevel(double minX, double maxX, double minY, double maxY, int imgWidth, int imgHeight) {
        int maxZoom = 18;
        double xRange = maxX - minX;
        double yRange = maxY - minY;
        double xZoom = xRange / imgWidth;
        double yZoom = yRange / imgHeight;

        double zoom = Math.min(xZoom, yZoom);
        for (int i = 0; i <= maxZoom; i++) {
            double resolution = 20037508.34 * 2 / (256 * Math.pow(2, i));
            if (resolution <= zoom) {
                return i;
            }
        }
        return maxZoom;
    }

    /**
     * 将给定的经纬度和缩放级别转换为WMTS瓦片坐标。
     *
     * @param lat  纬度
     * @param lon  经度
     * @param zoom 缩放级别
     * @return 对应的瓦片坐标[x, y]
     */
    public static int[] latLonToWmtsTile(double lat, double lon, int zoom) {
        int n = (int) Math.pow(2, zoom);
        int xTile = (int) Math.floor((lon + 180.0) / 360.0 * n);
        int yTile = (int) Math.floor((1.0 - Math.log(Math.tan(Math.toRadians(lat)) + 1.0 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2.0 * n);

        return new int[]{xTile, yTile};
    }


    /**
     * 根据指定的WMTS瓦片坐标下载瓦片图像。
     *
     * @param zoom 缩放级别
     * @param x 瓦片的X坐标
     * @param y 瓦片的Y坐标
     * @param layer WMTS图层
     * @param style WMTS样式
     * @param tileMatrixSet WMTS瓦片矩阵集
     * @throws IOException 当下载过程出现问题时抛出
     */
    public static void downloadWmtsTile(int zoom, int x, int y, String layer, String style, String tileMatrixSet) throws IOException {
        // 构建WMTS URL
        String tileUrl = String.format("http://sunbt.ltd:8080/geoserver/division/gwc/service/wmts?layer=division%%3A%s&style=%s&tilematrixset=%s&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%%2Fpng&TileMatrix=%s%%3A%d&TileCol=%d&TileRow=%d",
                layer, style, tileMatrixSet, tileMatrixSet, zoom, x, y);

        // 创建HTTP连接
        URL url = new URL(tileUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        // 设置User-Agent来模拟浏览器
        conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537");

        // 读取图像
        InputStream inStream = conn.getInputStream();
        BufferedImage image = ImageIO.read(inStream);

        // 保存图像
        ImageIO.write(image, "png", new File(String.format("E:\\code\\gitee\\gis-server\\data\\output\\wmts\\wmts\\%d_%d_%d.png", zoom, x, y)));

        // 将图像添加到已下载的瓦片列表
        downloadedTiles.add(image);
    }

    /**
     * 下载给定视窗内的所有WMTS瓦片图像。
     *
     * @param lat1 视窗的左上角纬度
     * @param lon1 视窗的左上角经度
     * @param lat2 视窗的右下角纬度
     * @param lon2 视窗的右下角经度
     * @param zoom 缩放级别
     * @param layer WMTS图层
     * @param style WMTS样式
     * @param tileMatrixSet WMTS瓦片矩阵集
     */
    public static WmtsTileInfo tilesInViewportForWMTS(double lat1, double lon1, double lat2, double lon2, int zoom, String layer, String style, String tileMatrixSet) {
        int[] tile1 = latLonToWmtsTile(lat1, lon1, zoom);
        int[] tile2 = latLonToWmtsTile(lat2, lon2, zoom);

        int xTile1 = tile1[0], yTile1 = tile1[1];
        int xTile2 = tile2[0], yTile2 = tile2[1];

        int xCount = Math.abs(xTile2 - xTile1) + 1;
        int yCount = Math.abs(yTile2 - yTile1) + 1;

        for (int x = Math.min(xTile1, xTile2); x <= Math.max(xTile1, xTile2); x++) {
            for (int y = Math.min(yTile1, yTile2); y <= Math.max(yTile1, yTile2); y++) {
                try {
                    downloadWmtsTile(zoom, x, y, layer, style, tileMatrixSet);
                    System.out.println(String.format("Downloaded WMTS tile at zoom=%d, x=%d, y=%d", zoom, x, y));
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println(String.format("Failed to download WMTS tile at zoom=%d, x=%d, y=%d", zoom, x, y));
                }
            }
        }
        return new WmtsTileInfo(xCount, yCount, zoom, layer, style, tileMatrixSet);
    }



    public static void demoForWMTSWebMercator() {
        try {
            String geoJsonStr = "{\n" +
                    "        \"coordinates\": [\n" +
                    "          [\n" +
                    "            [\n" +
                    "              120.49260317333017,\n" +
                    "              31.619643076821475\n" +
                    "            ],\n" +
                    "            [\n" +
                    "              120.49269519023414,\n" +
                    "              31.61621942444208\n" +
                    "            ],\n" +
                    "            [\n" +
                    "              120.49670146764805,\n" +
                    "              31.616339977720642\n" +
                    "            ],\n" +
                    "            [\n" +
                    "              120.49655282484696,\n" +
                    "              31.619727461000494\n" +
                    "            ],\n" +
                    "            [\n" +
                    "              120.49260317333017,\n" +
                    "              31.619643076821475\n" +
                    "            ]\n" +
                    "          ]\n" +
                    "        ],\n" +
                    "        \"type\": \"Polygon\"\n" +
                    "      }";
            JSONObject geoJsonObj = JSON.parseObject(geoJsonStr);
            JSONArray coordinates = geoJsonObj.getJSONArray("coordinates").getJSONArray(0);

            // 设置视图窗口的宽高
            int imageWidth = 640;
            int imageHeight = 480;

            // 创建图片对象
            BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, imageWidth, imageHeight);

            // 初始化最大、最小经度、纬度
            double minLng = Double.POSITIVE_INFINITY, maxLng = Double.NEGATIVE_INFINITY;
            double minLat = Double.POSITIVE_INFINITY, maxLat = Double.NEGATIVE_INFINITY;
            // 计算矢量数据的的最大、最小经度、纬度
            for (int i = 0; i < coordinates.size(); i++) {
                JSONArray point = coordinates.getJSONArray(i);
                double lng = point.getDoubleValue(0);
                double lat = point.getDoubleValue(1);
                minLng = Math.min(minLng, lng);
                maxLng = Math.max(maxLng, lng);
                minLat = Math.min(minLat, lat);
                maxLat = Math.max(maxLat, lat);
            }

            // 最佳层级
            int bestZoom = calculateZoomLevel(minLat, maxLat, minLng, maxLng, imageWidth, imageHeight);

            String layer = "320205";
            String style = "";
            String tileMatrixSet = "EPSG:900913";

            tilesInViewportForWMTS(minLat, minLng, maxLat, maxLng, bestZoom, layer, style, tileMatrixSet);


            int[] tile1 = latLonToWmtsTile(maxLat, minLng, bestZoom);
            int[] tile2 = latLonToWmtsTile(minLat, maxLng, bestZoom);

            String montage="E:\\code\\gitee\\gis-server\\data\\output\\wmts\\wmts\\montage.png";
            BufferedImage bufferedImage = stitchWmtsTiles(tile1[0], tile1[1], tile2[0], tile2[1], bestZoom, montage);
            System.out.println(bufferedImage.getWidth()+"::"+bufferedImage.getHeight());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        demoForWMTSWebMercator();
    }
}
